/*
Generic Doubly Linked List and Persistent Settings project
Author: Mark Newcomb Jr.

*/
#ifndef DLLIST_H
#define DLLIST_H

#include <string>

#ifndef NULL
#define NULL 0L
#endif

#if (MSC_VER > 1000)
#pragma once
#endif

#define MYDEBUG

//Here is a generic doubly-linked list class.  It doesn't have a ton of nifty features, but
//that's okay because it's a proof of concept to introduce me to linked lists and templates
//it should work fine for storing settings and the like
//Also, everything is declared inline because it's a template class


template<class T>
struct Node
	{
		T data;
		char* name;
	};



template<class T>
class DLList
{
public:	
struct LNode
		{
			T data;
			char* name;
			LNode* next;
			LNode* prev;
			LNode(T d,char* n, LNode* nt, LNode* p) : data(d),name(n),next(nt),prev(p){}
			

		};
	typedef LNode* ListNode;

public:

	
	DLList()
	{
		Head = NULL;
		Tail = NULL;
		CurrentNode = Head;
		NodeCount = 0; 
	}
	virtual ~DLList()
	{
	
		ListNode temp = Head;
		CurrentNode = Head;

		while(CurrentNode != NULL) //loops through every node, including head and tail and frees
			{						//the memory
		
				CurrentNode = CurrentNode->next;
				delete temp;
				temp = CurrentNode;
			}

	}

	
	//push a node with char name "n" and T data "d"
	//checks for nodes with same name if c = true. if it finds a 
	//node, it will delete that node before adding the desired node
	bool push_front(const char* n,T d, bool c = false)
	{ 
	bool retValue = false;
	
	Head = new LNode(d,const_cast<char*>(n),Head,NULL);

	if(Head->next)
		{
			Head->next->prev = Head;
			NodeCount++; 
			retValue = true;
		}
	
	if( (this->empty(false)) == true )
		{
			Tail = Head;
			retValue = false;
		}

	return retValue;
}
	
	Node<T> pop_front()
	{
		ListNode temp = Head;
		Node<T> retNode;

		retNode.data = Head->data;
		retNode.name = Head->name;
		
	
		Head = Head->next;
	
		if(Head)
		{
			Head->prev = NULL;
		}
		else
		{
			Tail = NULL;
		}

		NodeCount--;
		delete temp;
		return retNode;
	}
	
	Node<T> pop_back()
	{
		ListNode temp = Tail;
		Node<T> retNode;

		retNode.data = Tail->data;
		retNode.name = Tail->name;

		Tail = Tail->prev;
		if(Tail)
			{
				Tail->next = NULL;
			}
		else
			{
				Head = NULL;
			}
		delete temp;
		return retNode;
	}
	
	
	//deletes any settings that use the name n more than once, if keepFirst == true
	//then keeps the first name it finds, deletes the rest
	int delete_duplicates(char* n, bool keepFirst = true)
	{
		CurrentNode = Head;
		ListNode temp;
		int delCount = 0;
		bool foundFirst = false;
	
		while(CurrentNode != NULL)
		{
			if(keepFirst == true && foundFirst == false &&
				strcmp((const char*)CurrentNode->name,(const char*)n) == 0)
			{
				#ifdef DLLIST_VERBOSE
				std::cout<<"\nSetting foundFirst = true\n";
				#endif
				foundFirst = true;
				CurrentNode = CurrentNode->next;
				continue;
			}	
		
			if(strcmp((const char*)CurrentNode->name,(const char*)n) == 0)
			{
				#ifdef DLLIST_VERBOSE
				std::cout<<"\nThe names are the same.\n";
				#endif
				
				if(CurrentNode != Head)
					{ //it's not the Head node
						#ifdef DLLIST_VERBOSE
						std::cout<<"\n\tThe Current Node != Head.\n";
						#endif
						if(CurrentNode != Tail)
						{
						//it's not the tail node
						#ifdef DLLIST_VERBOSE
						std::cout<<"\n\tThe Current Node != Tail.\n";
						#endif
							temp = CurrentNode;
						
							CurrentNode->prev->next = CurrentNode->next;
							CurrentNode->next->prev = CurrentNode->prev;
				
							CurrentNode = CurrentNode->next;
							#ifdef DLLIST_VERBOSE
							std::cout<<"\n\tDELETE_DUP: temp = "<<temp<<" and CurrentNode = "<<CurrentNode<<"\n";
							#endif
							
							delete temp;
							delCount++;
						}
						else
						{
							//it is the tail node
							this->pop_back();
							CurrentNode = CurrentNode->next;
							#ifdef DLLIST_VERBOSE
							std::cout<<"\n\tDELETE_DUP: Current Node == Tail. Called pop_back()\n";
							#endif
							delCount++;
				
						}
					}
					else
					{
					//it is the head node
						this->pop_front();
						#ifdef DLLIST_VERBOSE
						std::cout<<"\n\tDELETE_DUP: CurrentNode == Head. Called pop_front()\n";
						#endif
						delCount++;
					}
			}
			else
			{
			#ifdef DLLIST_VERBOSE
			std::cout<<"\n\tDELETE_DUP: CurrentNode->name != given name.\n";
			#endif
			CurrentNode = CurrentNode->next;
			}
		}
		
	
	
	return delCount;
}
	
	
	
	Node<T> pop_name_front(const char* n)
	{
			{
			Node<T> retNode;
			CurrentNode = Head;
			ListNode temp;
			
			
			while(CurrentNode != NULL)
			{
		
		
				if(strcmp((const char*)CurrentNode->name,n) == 0)
				{
					if(CurrentNode == Head)
					{
						retNode.name = CurrentNode->name;
						retNode.data = CurrentNode->data;
						this->pop_front();
						#ifdef DLLIST_VERBOSE
						std::cout<<"\nIn POP_NAME: Deleted a node.\n";
						#endif
						break;
					}
					else if(CurrentNode == Tail)
					{
						retNode.name = CurrentNode->name;
						retNode.data = CurrentNode->data;
						this->pop_back();
						
						#ifdef DLLIST_VERBOSE
						std::cout<<"\nIn POP_NAME: Deleted a node.\n";
						#endif
						
						break;
					}
					else
					{
						temp = CurrentNode;
					
						retNode.name = CurrentNode->name;
						retNode.data = CurrentNode->data;
				
						CurrentNode->prev->next = CurrentNode->next;
						CurrentNode->next->prev = CurrentNode->prev;
				
						delete temp;
						#ifdef DLLIST_VERBOSE
						std::cout<<"\nIn POP_NAME: Deleted a node.\n";
						#endif
						break;
					}
			
				}
				else
				{
				CurrentNode = CurrentNode->next;
				#ifdef DLLIST_VERBOSE
				std::cout<<"\n\tPOP_NAME: Skipping this node.\n";
				#endif
				}
	
			}
		return retNode;
		}
	}
	
	//move the CurrentNode through the list until it gets to the index.  The bool prev
	//can be used to go to the node previous to index.  true = go to previous node
	//bool move_to(ListNode index, bool prev = false); 

	//returns true if empty, false if there is still some value
	//checkCount specifies whether or not we want to check the NodeCount when 
	//determining if the list is empty.  Otherwise just check whether Head or Tail == NULL
	bool empty(bool checkCount = true)
	{
		bool retValue = false;

		if(checkCount == true)
		{

			if(!Head ||!Tail || (NodeCount <= 0))
				retValue = true;
		}
		else
		{
			if(!Head || !Tail)
				retValue = true;
		}
	

		return retValue;
	}

									

	#ifdef MYDEBUG
	void dump_to_screen(){
		std::cout<<"DLLIST->dump_to_screen().\n";
		Node<T> tempnode;
		while( !(this->empty(false)) )
		{	
			tempnode = this->pop_front();
			std::cout<<"\tName: "<<tempnode.name<<" Data: "<<tempnode.data<<"\n";
		}
		}
	#endif

private:
	
	//bool move_back(ListNode index);//moves to the node previous to index

	
	int NodeCount; //used for redundancy when checking if list is empty
	ListNode CurrentNode;
	ListNode Head;
	ListNode Tail;
}; 


#endif //DLLIST_H